package fast.calc;

import indzcraft.loader.Spec;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Attacks {
	
	Fast f;
	Stats s;
	int playerLevel = 85; // FIXME LEVEL_90
	double spellScaling85 = 1125.227416992187500;
	double spellScaling90 = 1246.298583984375000;
	
	double spellScaling;
	
	public Attacks(Fast f){
		this.f = f;
		s = f.getStats();
		if(playerLevel == 85)
			spellScaling = spellScaling85;
		else
			spellScaling = spellScaling90;
	}
	
	public AttacksBlock get_dmg_sinister_strike(){
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * w.getDps() + w.getNormalizedSpeed() * s.getApBuffed() / 14.;
		double mod = 1.45;
		double base_damage = 0.1780000031 * spellScaling;
		damage = (damage + base_damage) * mod;
		if(s.getSpec() == Spec.Combat && s.getGearBuffs().has("dagger_combat_buff"))
			damage *= 1.45;
		if(s.getGearBuffs().hasSet("t14_2pc"))
			damage *= 1.15;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("sinister_strike",AttackType.physical, damage, crit_damage);
	}
	
	public double get_crit_sinister_strike(){
		double crit_chance = get_crit_mh();
		if(s.getGearBuffs().hasSet("t11_2pc")) 
			crit_chance += 0.05;
		return crit_chance;
	}
	
	public AttacksBlock get_dmg_main_gauche(){
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * w.getDps() + w.getNormalizedSpeed() * s.getApBuffed() / 14.;
		double mod = 1.2;
		damage *= mod;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("main_gauche",AttackType.physical,damage, crit_damage);
	}
	
	public AttacksBlock get_dmg_eviscerate(){
		double avg = 0.5929999948 * spellScaling;
		double min = avg * ( 1 - 1.0 / 2);
		double max = avg * ( 1 + 1.0 / 2);
		double bonus_damage = 0.7860000134 * spellScaling;
		
		double damage = ((min+max)/2 + (bonus_damage + s.getApBuffed() * 0.16) * 5);
		if(s.getSpec() == Spec.Combat)
			damage *= 1.35;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("eviscerate",AttackType.physical,damage, crit_damage);
	}
	
	public AttacksBlock get_dmg_instant_poison(){
		double base_damage = 0.3129999936 * spellScaling;
		double damage = base_damage + 0.109 * s.getApBuffed();
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("instant_poison",AttackType.magical,damage, crit_damage);
	}
	
	public AttacksBlock get_dmg_deadly_poison(){
		double base_damage = 0.6000000238 * spellScaling;
		double damage = base_damage + 0.213 * s.getApBuffed();
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("deadly_poison",AttackType.magical,damage, crit_damage);
	}
	
	public double get_crit_main_gauche(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_revealing_strike(){
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * ( w.getDps() + s.getApBuffed() / 14.);
		damage *= 1.25;
		if(s.getSpec() == Spec.Combat && s.getGearBuffs().has("dagger_combat_buff"))
			damage *= 1.45;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("revealing_strike",AttackType.physical,damage, crit_damage);
	}
	
	public AttacksBlock get_dmg_ks_mh(){
		Stats s = f.getStats();
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * ( w.getDps() + s.getApBuffed() / 14.);
		damage *= 1.5;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("ks_mh",AttackType.physical,damage, crit_damage);
	}
	
	public AttacksBlock get_dmg_ks_oh(){
		Stats s = f.getStats();
		WeaponStats w = s.getOh();
		double damage = w.getSpeed() * ( w.getDps() + s.getApBuffed() / 14.);
		double mod = 0.5;
		if(s.getSpec() == Spec.Combat)
			mod = 0.875;
		damage *= mod;
		damage *= 1.5;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("ks_oh",AttackType.physical,damage, crit_damage);
	}
	
	public double get_crit_ks_mh(){
		return get_crit_mh();
	}
	
	public double get_crit_ks_oh(){
		return get_crit_oh();
	}
	
	public AttacksBlock get_dmg_mh(){
		Stats s = f.getStats();
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * ( w.getDps() + s.getApBuffed() / 14.);
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("mh",AttackType.physical,damage, crit_damage);
	}
	
	public AttacksBlock get_dmg_oh(){
		Stats s = f.getStats();
		WeaponStats w = s.getOh();
		double damage = w.getSpeed() * ( w.getDps() + s.getApBuffed() / 14.);
		double mod = 0.5;
		if(s.getSpec() == Spec.Combat)
			mod = 0.875;
		damage *= mod;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("oh",AttackType.physical,damage, crit_damage);
	}
	
	public double get_crit_mh(){
		double crit_chance = s.getCritPercent();
		return crit_chance;
	}
	
	public double get_crit_oh(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_sb_mh(){
		Stats s = f.getStats();
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * ( w.getDps() + s.getApBuffed() / 14.);
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("sb_mh",AttackType.magical,damage, crit_damage);
	}
	
	public AttacksBlock get_dmg_sb_oh(){
		Stats s = f.getStats();
		WeaponStats w = s.getOh();
		double damage = w.getSpeed() * ( w.getDps() + s.getApBuffed() / 14.);
		double mod = 0.5;
		if(s.getSpec() == Spec.Combat)
			mod = 0.875;
		damage *= mod;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("sb_oh",AttackType.magical,damage, crit_damage);
	}
	
	public double get_crit_sb_mh(){
		return get_crit_mh();
	}
	
	public double get_crit_sb_oh(){
		return get_crit_oh();
	}
	
	public double get_crit_eviscerate(){
		return get_crit_mh();
	}
	
	public double get_crit_revealing_strike(){
		return get_crit_mh();
	}
	
	public double get_crit_instant_poison(){
		return get_crit_mh();
	}
	
	public double get_crit_deadly_poison(){
		return get_crit_instant_poison();
	}
	
	public AttacksBlock get_dmg_envenom(){
		double base_damage = 0.3210000098 * spellScaling;
		double damage = (base_damage + s.getApBuffed( )* 0.112) * 5;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("envenom",AttackType.magical,damage, crit_damage);
	}
	
	public double get_crit_envenom(){
		return get_crit_instant_poison();
	}
	
	public AttacksBlock get_dmg_rupture(){
		double base_damage = 0.1850000024 * spellScaling;
		double bonus_damage = 0.0260000005 * spellScaling;
		
		double damage = base_damage + bonus_damage * 5 + 0.062 * s.getApBuffed();
		if(s.getSpec() == Spec.Subtlety)
			damage *= 1.5;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("rupture",AttackType.bleed, damage, crit_damage);
	}
	
	public double get_crit_rupture(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_venomous_wound(){
		double base_damage = 0.5500000119 * spellScaling;
		double damage = base_damage + 0.1600000 * s.getApBuffed();
		if(s.getGearBuffs().hasSet("t14_2pc"))
			damage *= 1.20;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("venomous_wound",AttackType.magical, damage, crit_damage);
	}
	
	public double get_crit_venomous_wound(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_mutilate_mh(){
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * w.getDps() + w.getNormalizedSpeed() * s.getApBuffed() / 14.;
		double mod = 2.0;
		double bonus_damage = 0.1790000051 * spellScaling;
		damage = (damage + bonus_damage) * mod;
		if(w.getType() != WeaponType.dagger)
			damage = 0.0;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("mutilate_mh",AttackType.physical, damage, crit_damage);
	}
	
	public AttacksBlock get_dmg_mutilate_oh(){
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * w.getDps() + w.getNormalizedSpeed() * s.getApBuffed() / 14.;
		double mod = 2.0;
		mod *= 0.5; // offhand penalty
		double base_damage = 0.1790000051 * spellScaling;
		damage = (damage + base_damage) * mod;
		if(w.getType() != WeaponType.dagger)
			damage = 0.0;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("mutilate_oh",AttackType.physical, damage, crit_damage);
	}
	
	public double get_crit_mutilate_mh(){
		double crit_chance = get_crit_mh();
		if(s.getGearBuffs().hasSet("t11_2pc")) 
			crit_chance += 0.05;
		return crit_chance;
	}
	
	public double get_crit_mutilate_oh(){
		double crit_chance = get_crit_oh();
		if(s.getGearBuffs().hasSet("t11_2pc")) 
			crit_chance += 0.05;
		return crit_chance;
	}
	
	public AttacksBlock get_dmg_dispatch(){
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * w.getDps() + w.getNormalizedSpeed() * s.getApBuffed() / 14.;
		double mod = 4.0;
		double base_damage = 0.3899999857 * spellScaling;
		damage = (damage + base_damage) * mod;
		if(w.getType() != WeaponType.dagger)
			damage = 0.0;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("dispatch",AttackType.physical, damage, crit_damage);
	}
	
	public double get_crit_dispatch(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_hemorrhage(){
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * w.getDps() + w.getNormalizedSpeed() * s.getApBuffed() / 14.;
		double mod = 1.4;
		if(w.getType() == WeaponType.dagger)
			mod = 2.03;
		damage = damage * mod;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("hemorrhage",AttackType.physical, damage, crit_damage);
	}
	
	public double get_crit_hemorrhage(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_hemorrhage_dot(){
		AttacksBlock hemo_direct = get_dmg_hemorrhage();
		double hemo_direct_crit_chance = get_crit_hemorrhage();
		double hit = hemo_direct.getHitDamage() * ( 1 - hemo_direct_crit_chance);
		double crit = hemo_direct.getCritDamage() * hemo_direct_crit_chance;
		
		double damage = (hit + crit) / 8;
		double mod = 0.5;
		damage = damage * mod;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("hemorrhage_dot",AttackType.bleed, damage, crit_damage);
	}
	
	public double get_crit_hemorrhage_dot(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_backstab(){
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * w.getDps() + w.getNormalizedSpeed() * s.getApBuffed() / 14.;
		double mod = 2.75;
		double base_damage = 0.3070000112 * spellScaling;
		damage = (damage + base_damage) * mod;
		if(s.getGearBuffs().hasSet("t14_2pc"))
			damage *= 1.10;
		if(w.getType() != WeaponType.dagger)
			damage = 0.0;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("backstab",AttackType.physical, damage, crit_damage);
	}
	
	public double get_crit_backstab(){
		double crit_chance = get_crit_mh();
		if(s.getGearBuffs().hasSet("t11_2pc")) 
			crit_chance += 0.05;
		return crit_chance;
	}
	
	public AttacksBlock get_dmg_ambush(){
		WeaponStats w = s.getMh();
		double damage = w.getSpeed() * w.getDps() + w.getNormalizedSpeed() * s.getApBuffed() / 14.;
		double mod = 3.25;
		double base_damage = 0.5000000000 * spellScaling;
		if(w.getType() == WeaponType.dagger)
			mod *= 1.447;
		damage = (damage + base_damage) * mod;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("ambush",AttackType.physical, damage, crit_damage);
	}
	
	public double get_crit_ambush(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_vial_of_shadows(){
		double damage = 5035 + 0.3 * s.getApBuffed();
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("vial_of_shadows",AttackType.physical, damage, crit_damage);
	}
	
	public double get_crit_vial_of_shadows(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_vial_of_shadows_heroic(){
		double damage = 5682.5 + 0.339 * s.getApBuffed();
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("vial_of_shadows_heroic",AttackType.physical, damage, crit_damage);
	}
	
	public double get_crit_vial_of_shadows_heroic(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_vial_of_shadows_lfr(){
		double damage = 4460.5 + 0.266 * s.getApBuffed();
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("vial_of_shadows_lfr",AttackType.physical, damage, crit_damage);
	}
	
	public double get_crit_vial_of_shadows_lfr(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_no_kaled(){
		double damage = (7654 + 11481)/2;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("no_kaled",AttackType.magical, damage, crit_damage);
	}
	
	public double get_crit_no_kaled(){
		return get_crit_mh();
	}
	
	public AttacksBlock get_dmg_no_kaled_heroic(){
		double damage = (8640 + 12960)/2;
		double crit_damage = damage * s.getCritMultiplier();
		return new AttacksBlock("no_kaled_heroic",AttackType.magical, damage, crit_damage);
	}
	
	public double get_crit_no_kaled_heroic(){
		return get_crit_mh();
	}

	public AttacksBlock get_dmg(String attack) {
		Method method;
		AttacksBlock value = null;
		try {
			method = this.getClass().getMethod( "get_dmg_" + attack );
			value = (AttacksBlock) method.invoke( this );
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return value;
	}
	
	public Double get_crit(String attack) {
		Method method;
		Double value = null;
		try {
			method = this.getClass().getMethod( "get_crit_" + attack );
			value = (Double) method.invoke( this );
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return value;
	}
}
